package at.technikum_wien.embsys.aat.PriscillaCore.component;

import java.util.Iterator;
import java.util.List;

import at.technikum_wien.embsys.aat.PriscillaCore.enums.DayNightState;
import at.technikum_wien.embsys.aat.PriscillaCore.enums.EnoceanComponentType;
import at.technikum_wien.embsys.aat.PriscillaCore.event.FrameEventHumidity;
import at.technikum_wien.embsys.aat.PriscillaCore.event.FrameEventTemperature;
import at.technikum_wien.embsys.aat.PriscillaCore.link.event.LinkListener;
import at.technikum_wien.embsys.aat.PriscillaCore.telegram.Telegram;



/**
 * 
 * EEP_07_10_14 component class - room operating panel
 *
 * Type: Temperature sensor, humidity and day/night control
 * Temperature max: 40°C
 * Temperature min: 0°C
 * Humidity max: 100%
 * Humidity min: 0%
 * Day/night switch
 * 
 * @author B. Aigner
 * 
 */
public class EEP_07_10_14 implements EnoceanComponent {
	
	Telegram telegram;
	List<?> eventListener;
	
	float humidity = -300F;
	float temperature = -300F;
	DayNightState dayNight = DayNightState.INITIAL_STATE;
	
	EnoceanComponentType enoceanComponentType = EnoceanComponentType.EEP_07_10_14;
	boolean telegramCreation = false;
	
	int humidity_temp;
	float humidity_max = 100;
	float humidity_min = 0;
	int humidity_value_max = 250;
	int humidity_value_min = 0;
	
	int temperature_temp;
	float temperature_max = 40;
	float temperature_min = 0;
	int temperature_value_max = 250;
	int temperature_value_min = 0;
	
	float multiplier;

	/**
	 * This constructor is used for received packets.
	 * It will parse the telegram and set the corresponding members.
	 * 
	 * @param telegram Received Telegram
	 * @param listener Event subscriber list
	 */
	public EEP_07_10_14(Telegram telegram, List<?> listener) {
		super();
		this.telegram = telegram;
		this.eventListener = listener;
		
		humidity_temp = telegram.getData2();
		temperature_temp = telegram.getData1();

		if(checkBit(telegram.getData0(),0)) {
			dayNight = DayNightState.DAY;
		} else {
			dayNight = DayNightState.NIGHT;
		}

		multiplier = (humidity_max - humidity_min) / (humidity_value_max - humidity_value_min);

		if(multiplier >= 0) {
			this.humidity = multiplier * humidity_temp + humidity_max;
		} else {
			this.humidity = multiplier * humidity_temp + humidity_min;
		}

		multiplier = (temperature_max - temperature_min) / (temperature_value_max - temperature_value_min);

		if(multiplier >= 0) {
			this.temperature = multiplier * temperature_temp + temperature_max;
		} else {
			this.temperature = multiplier * temperature_temp + temperature_min;
		}
		fireFrame();
	}

	/**
	 * Fire all associated events
	 */
	void fireFrame() {
		Iterator<?> listen = eventListener.iterator();
		final FrameEventTemperature frameEventTemperature = new FrameEventTemperature(this, telegram.getDeviceID(), this.temperature);
		while (listen.hasNext()) {
			((LinkListener) listen.next()).frameReceived_Temperature(frameEventTemperature);
		}
		
		//TODO: Day/night Event
		/*listen = eventListener.iterator();
		final FrameEventBinary frameEventBinary = new FrameEventBinary(this, telegram.getDeviceID(), this.occupancy, BinaryState.FALSE, BinaryState.FALSE, BinaryState.FALSE);
		while (listen.hasNext()) {
			((LinkListener) listen.next()).frameReceived_Binary(frameEventBinary);
		}*/
		
		listen = eventListener.iterator();
		final FrameEventHumidity frameEventHumidity = new FrameEventHumidity(this, telegram.getDeviceID(), this.humidity);
		while (listen.hasNext()) {
			((LinkListener) listen.next()).frameReceived_Humidity(frameEventHumidity);
		}
	}

	/**
	 * Void constructor. It is used to create a new telegram for sending
	 */
	public EEP_07_10_14() {
		super();
		telegram = new Telegram();
		telegramCreation = true;
	}

	/**
	 * This constructor is used to create a telegram (the device id is given as parameter)
	 * @param deviceID Full device ID
	 */
	public EEP_07_10_14(String deviceID) {
		super();
		telegram = new Telegram();
		telegram.setDeviceID(deviceID);
		telegramCreation = true;
	}
	
	boolean checkBit(int value, int bitNr) {
		return (value & (1 << bitNr)) != 0;
	}
	
	int setBit(int value, int bitNr) {
		return value | (1<<bitNr);
	}
	
	int clrBit(int value, int bitNr) {
		return value & (~(1<<bitNr));
	}
	
	@Override
	public String getDeviceID() {
		return telegram.getDeviceID();
	}
	@Override
	public String getORG() {
		return telegram.getxORG();
	}
	@Override
	public EnoceanComponentType getType() {
		return enoceanComponentType;
	}

	@Override
	public void setDeviceID(String deviceID) {
		telegram.setDeviceID(deviceID);
	}

	/**
	 * Creates a new telegram from the component
	 * 
	 * This method creates a new telegram (if the component is initialized with
	 * a constructor without a telegram as parameter) or returns the saved telegram.
	 * The new telegram is built up from the members in this class (in this case the temperature, humidity
	 * and day/night control). 
	 * @return Created telegram
	 */
	public Telegram getTelegram() {
		if(telegramCreation == true) {
			multiplier = (temperature_max - temperature_min) / (temperature_value_max - temperature_value_min);
			if(multiplier < 0) {
				telegram.setData1((int)((this.temperature - temperature_max) / multiplier));
			} else {
				telegram.setData1((int)((this.temperature - temperature_min) / multiplier));
			}
			
			multiplier = (humidity_max - humidity_min) / (humidity_value_max - humidity_value_min);
			if(multiplier < 0) {
				telegram.setData2((int)((this.humidity - humidity_max) / multiplier));
			} else {
				telegram.setData2((int)((this.humidity - humidity_min) / multiplier));
			}
			
			if(dayNight == DayNightState.NIGHT) {
				telegram.setData0(clrBit(telegram.getData0(),0));
			} else {
				telegram.setData0(setBit(telegram.getData0(),0));
			}
		}
		return telegram;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getTemperature() {
		return temperature;
	}

	public DayNightState getDayNight() {
		return dayNight;
	}
	
	public void setDayNight(DayNightState dayNight) {
		this.dayNight = dayNight;
	}
	
	public void setHumidity(float humidity) {
		if(humidity > humidity_max) return;
		if(humidity < humidity_min) return;
		this.humidity = humidity;
	}
	
	public void setTemperature(float temperature) {
		if(temperature > temperature_max) return;
		if(temperature < temperature_min) return;
		this.temperature = temperature;
	}
}
